#include "matrix.h"
#include "vector.h"

#include <cstdlib>

Matrix::Matrix(const unsigned int& _row,const unsigned int& _column, const unsigned int& _type) : row(_row), column(_column) {
    data = new double*[row];

    for(unsigned int i = 0 ; i < row ; i++)
        data[i] = new double[column];

    for(unsigned int i = 0; i < row; i++)
		for(unsigned int j = 0; j < column; j++)
			data[i][j] = 0;

    switch(_type) {
		case NORMAL_MATRIX:
			break;
		case IDENTITY_MATRIX:
			if(row != column)
				exit(1);
			for(unsigned int i = 0;i<row;i++)
				data[i][i] = 1;
			break;
    }
}

Matrix::Matrix(const Matrix& _m) {
	row = _m.row;
	column = _m.column;

	data = new double*[row];

	for(unsigned int i = 0; i < row; i++)
		data[i] = new double[column];

	for(unsigned int i = 0; i < row; i++)
		for(unsigned int j = 0; j < column; j++)
			data[i][j] = _m[i][j];
}

Matrix::~Matrix(void) {
    for(unsigned int i = 0; i < row; i++)
        delete[] data[i];
    delete[] data;
}

unsigned int Matrix::getRow(void) const {
    return row;
}

unsigned int Matrix::getColumn(void) const {
    return column;
}

void Matrix::print(void) const {
    for(unsigned int i=0 ; i<row ; i++) {
        std::cout << "|\t";
        for(unsigned int j=0 ; j<column ; j++)
            std::cout << data[i][j] << "\t";
        std::cout << "|" << std::endl;
    }
}

double* Matrix::operator[](unsigned int i) const {
    return data[i];
}

Matrix Matrix::operator-(void) const {
    Matrix result(row,column);

    for(unsigned int i=0; i<row; i++)
        for(unsigned int j=0; j<column; j++)
            result[i][j] = - data[i][j];

    return result;
}

Matrix Matrix::meanColumn(Matrix *_mean) const {
    Matrix result(row, column);
    double mean;
    for(unsigned int j = 0; j < column; j++){
        mean = 0;
        for(unsigned int i = 0; i < row; i++){
            mean += data[i][j];
        }
        for(unsigned int i = 0; i < row; i++){
            result[i][j] = mean/row;
        }
        if(_mean) (*_mean)[0][j] = mean/row;
    }
    return result;
}

Matrix Matrix::getOneRow(unsigned int _i) const {
    Matrix result(1, column);

    for(unsigned int j = 0; j < column; j++){
        result[0][j] = data[_i][j];
    }
    return result;
}

Matrix Matrix::covarianceMatrix(void) const {
	Matrix result(column, column);
//        std::cout << "coluna :" << column <<std::endl;

	double x, z, xz, ex, ez, exz;
	for(unsigned int j = 0; j < column; j++){
//        std::cout << "j :" << j <<std::endl;
		for(unsigned int k = column; k > j; k--){
//            std::cout << "k :" << k <<std::endl;

			x = z = xz = ex = ez = exz = 0.0;
			for(unsigned int i = 0; i < row; i++){
				x += data[i][j];
				z += data[i][k - 1];
				xz += data[i][j]*data[i][k - 1];
			}
			ex = x/row;
			ez = z/row;
			exz = xz/row;
//			std::cout << "  ex: " << ex << "  ez: " << ez << "  exz: " << exz << "  result " << exz - (ex*ez) << std::endl;
//			std::cin >> nha;
			result[j][k - 1] = exz - (ex*ez);
			result[k - 1][j] = exz - (ex*ez);
		}
	}
//	std::cout << "calculdo tudo" <<std::endl;
//	result.print();
	return result;
}

Matrix Matrix::covarianceMatrix2(void) const {
	Matrix result(column, column);
	Vector v(column);
	Vector x(column);

	for(unsigned int j = 0; j < column; j++){
		for(unsigned int i = 0; i < row; i++){
			x[j] += data[i][j];
		}
		x[j] /= row;
	}
	std::cout << "calculdo mean row:" << row <<std::endl;

	for(unsigned int i = 0; i < row; i++){
		v = (*this).transpose().toVector(i) - x;
		result = result + ( v.outterProduct(v)  );

		if(i%100 == 0)
            std::cout << "i :" << i <<std::endl;
	}
	std::cout << "calculdo tudo" <<std::endl;
	result = result*(1.0/(row));//pode ser que tenha o row - 1....
	return result;
}

/*void Matrix::svd(Matrix *u, Matrix *s, Matrix *v) const {
	double error = 0.00001;
	Matrix aat = (*this) * (*this).transpose();
	QR method(aat, error);
	u = method.getLambda();
	s = method.getX();

	Matrix ata = (*this).transpose() * (*this);
	QR method2(ata, error);
	v = method2.getLambda().traspose();


	std::cout << "u: " << std::endl;
	u->print();
	std::cout << "s: " << std::endl;
	s->print();
	std::cout << "v: " << std::endl;
	v->print();

}*/

Matrix Matrix::transpose(void) const {
	Matrix result(column, row);

	for(unsigned int i = 0; i < row; i++)
	for(unsigned int j = 0; j < column; j++)
		result[j][i] = (*this)[i][j];
	return result;
}

Vector Matrix::toVector(unsigned int _column) const {
	Vector result(row);

	for(unsigned int i = 0; i < row; i++) {
		result[i] = data[i][_column];
	}

	return result;
}

Matrix& Matrix::operator= (const Matrix& _m) {
    for(unsigned int i = 0; i < row; i++)
        delete[] data[i];
    delete[] data;

    row = _m.getRow();
    column = _m.getColumn();

    data = new double*[row];

    for(unsigned int i = 0; i < row; i++)
        data[i] = new double[column];

    for(unsigned int i = 0; i < row; i++)
        for(unsigned int j = 0; j < column; j++)
            data[i][j] = _m[i][j];

    return (*this);
}


Matrix operator+(const Matrix& _matrix1,const Matrix& _matrix2) {
    if( _matrix1.getRow() == _matrix2.getRow()  && _matrix1.getColumn() == _matrix2.getColumn()) {
        unsigned int row = _matrix1.getRow();
        unsigned int column = _matrix1.getColumn();
        Matrix result(row,column);

        for(unsigned int i=0; i<row; i++)
            for(unsigned int j=0; j<column; j++)
                result[i][j] = _matrix1[i][j] + _matrix2[i][j];

        return result;
    }
    else {
        std::cout <<"Algo errado com as operações de matrizes" << std::endl;
        exit(1);
    }
}

Matrix operator-(const Matrix & _matrix1,const Matrix & _matrix2) {
    if( _matrix1.getRow() == _matrix2.getRow()  && _matrix1.getColumn() == _matrix2.getColumn()) {
        unsigned int row = _matrix1.getRow();
        unsigned int column = _matrix1.getColumn();
        Matrix result(row,column);

        for(unsigned int i=0; i<row; i++)
            for(unsigned int j=0; j<column; j++)
                result[i][j] = _matrix1[i][j] - _matrix2[i][j];

        return result;
    }
    else {
        std::cout <<"Algo errado com as operações de matrizes" << std::endl;
        exit(1);
    }
}


Matrix operator*(const Matrix & _matrix1,const Matrix & _matrix2) {
    if(_matrix1.getColumn() != _matrix2.getRow()) {
        std::cout <<"Algo errado com as operações de matrizes" << std::endl;
        exit(1);
    }
    unsigned int row = _matrix1.getRow();
    unsigned int column = _matrix2.getColumn();
    Matrix result(row,column);

    for(unsigned int i = 0; i < row; i++) {
        for(unsigned int j = 0; j < column; j++) {
            result[i][j] = 0;
            for(unsigned int k = 0; k < _matrix1.getColumn(); k++)
                result[i][j] = result[i][j] + _matrix1[i][k] * _matrix2[k][j];
        }
    }
    return result;
}

Matrix operator*(const Matrix& _matrix,double _value) {
    Matrix result = _matrix;
    for(unsigned int i =0; i < result.getRow(); i++) {
        for(unsigned int j = 0; j < result.getColumn(); j++) {
                result[i][j] = _matrix[i][j]*_value;
        }
    }
    return result;
}

